<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <title>画柱状图-面向对象</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link type="text/css" rel="stylesheet" href="./index.css">
  <script src="https://cdn.staticfile.org/konva/2.6.0/konva.js"></script>
</head>
  <body>
    <!-- <canvas id="myCanvas" width="600" height="600">
      你的浏览器不支持canvas，请升级浏览器
    </canvas> -->
    <div id="con"></div>
  </body>
</html>

<script>


  class histogramChart {
    constructor(options){
      this.x = options.x || 0;
      this.y = options.y || 0; // 柱状图的原点坐标
      this.width = options.width || 0; // 柱状图的总宽度
      this.height = options.height || 0;
      this.data = options.data || []; // 渲染图表页面的数据

      let x0 = 0;
      let y0 = 0;
      // 柱状图中的所有元素组
      this._group();
      // 放矩形的组
      this._createGroup({name: 'rectGroup', x:0, y:0});
      // 放百分比文字的组
      this._createGroup({name: 'textGroup', x:0, y:0});
      this._createLine(); // 底线
      this._eachData(); // 循环创建图形
    }
    // 创建一个总组管理
    _group (){
      this.group = new Konva.Group({
        x: this.x,
        y: this.y,
      })
    }
    // 创建组函数
    _createGroup (options){
      let flag = options.merge || true; // 是否合并到总组中
      // 创建当前组
      this[options.name] = new Konva.Group({
        x: options.x,
        y: options.y,
      })
      if(flag){ // 合并进总组
        this.group.add( this[options.name] )
      }
    }
    // 创建底线条
    _createLine (){
      let bsLine = new Konva.Line({
        // 前二参数起始坐标，后面画到指定坐标位置处（可以无限画）
        points: [0,0, this.width, 0],
        stroke: 'lightgreen', // 线的颜色
        strokeWidth: 2, // 线的宽度
      })
      this.group.add(bsLine);
    }
    // 循环创建数据
    _eachData (){
      let data = this.data;
      // 定义矩形每个占的空间
      let rectWidth = this.width / this.data.length;
      let height = this.height; // 每个数据的高度
      //循环创建每个柱状图数据
      data.forEach( (item,index) => {
        // 创建矩形-柱状图
        let react = new Konva.Rect({
          // x坐标位置占矩形位置在加二边空距，我是定了四分之一
          x: (1/4 + index) * rectWidth,
          y: - (item.value * height), // 调整每个y轴位置
          width: rectWidth/2, // 画宽度占一半，其它留空
          // 因为高度是向下的，所以要把y轴向上移动
          height: item.value * height, // 矩形高度，最大宽度*占比
          fill: item.color,
          opacity: .8,
          cornerRadius: 25, //圆角
        })
        this.rectGroup.add(react); // 添加到矩形组

        // 把百分比文字放到 柱状图的顶部文字
        let text = new Konva.Text({
          x: (index * rectWidth),
          y: - (item.value * height) - 14, // 调整每个y轴位置
          fontSize: 14, // 字体大小
          width: rectWidth, // 宽度配合文字居中
          align: 'center',
          fill: item.color, // 颜色
          name: 'textPercent',
          text: `${item.value*100}%` // 内容
        })
        this.textGroup.add(text); // 添加到文字组

        // 底部文字内容
        let textBottom = new Konva.Text({
          x: (1/4+index) * rectWidth,
          y: 0, // 调整每个y轴位置
          fontSize: 14, // 字体大小
          width: rectWidth, // 宽度配合文字居中
          // align: 'center',
          fill: item.color, // 颜色
          rotation: 30, // 旋转
          text: item.name // 内容
        })
        this.textGroup.add(textBottom); // 添加到组
      });

    }
    // 做一个动画效果事件
    playAnimation(){
      let data = this.data;
      let height = this.height;
      let duration = 2; // 动画持续时间

      // 拿到所有矩形,让柱状图进行动画展示
      this.rectGroup.getChildren().each( (item,index)=>{
        // 让状态图中所有矩形 高0,y0
        // 在konva中不传入参数就是读取值,传入就是改变值
        item.y(0); // 设置每个矩形的y轴,因为在组中,所以写0继承了值
        item.height(0);
        // 动画显示到初始状态
        item.to({
          duration: duration,
          y: - (data[index].value * height),
          height: data[index].value * height,
        })
      });

      // 顶部文字
      layer.find('.textPercent').each( (item,index)=>{
        item.y(0 - 14); // 设置y轴,确保一直在柱状图顶部固定
        // 动画显示到初始状态
        item.to({
          duration: duration,
          y: - (data[index].value * height) -14,
        })
      });
    }

    addToGrupOrlayer( arg ){
      arg.add( this.group );
    }
  }
</script>
<script>
  let data = [
    {name:'前端', value: '.8', color: 'green'},
    {name:'PHP', value: '.3', color: 'blue'},
    {name:'Java', value: '.7', color: 'red'},
    {name:'UI', value: '.9', color: 'orange'},
    {name:'IOS', value: '.4', color: 'purple'},
    {name:'Android', value: '.9', color: 'pink'},
  ]
 // 创建舞台
 let stage = new Konva.Stage({
    container: 'con', // 放到哪个div的容器内显示
    width: window.innerWidth, // 等于屏幕宽度
    height: window.innerHeight // 等于屏幕高度
  })
  let layer = new Konva.Layer();// 创建一个层
  stage.add( layer ); // 把层添加到舞台上去

  let h = new histogramChart({
    // 设置起始xy坐标，以百分比，这样兼容各平台显示
    x: 1/8 * stage.width(),
    y: 3/4 * stage.height(),
    // 设置柱状图最大宽高度
    width: 3/4 * stage.width(),
    height: 1/2 * stage.height(),
    // 数据
    data: data,
  })
  h.addToGrupOrlayer( layer ); // 添加到层上面去
  // 把层渲染到舞台上
  layer.draw();

  // 在舞台上点击任何区域的时候都会触发此事件
  stage.on('contentClick', ()=>{
    h.playAnimation(); // 执行动画
  })
</script>
